package com.lyf.scm.plan.domain.service.impl;

import java.util.concurrent.TimeUnit;

import com.lyf.scm.plan.domain.service.IDistributedLockService;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import lombok.extern.slf4j.Slf4j;

/**
 * @author Lin.Xu - 2020年1月3日[下午3:09:17]
 * @Title: DistributedLockServiceImpl.java
 * @Description: 分布式锁
 */
@Slf4j
@Service
public class DistributedLockServiceImpl implements IDistributedLockService {

    //单位：Ms
    public static final int DEFLOCK_EXPIRE = 300;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * @param lockKey    加密键值
     * @param expire 有效时间
     * @throws
     * @Title: tryLock
     * @Description: 加锁操作
     * @return: Boolean
     * @author Lin.Xu - 2020年1月3日
     */
    @Override
    public Boolean tryLock(String lockKey, Long expire) {
        log.info("key:{}:expire:{}--开始加锁", lockKey, expire);
        Long endTime = System.currentTimeMillis() + (null == expire ? DEFLOCK_EXPIRE + 1 : expire + 1);
        // 这个其实就是setnx命令，只不过在java这边稍有变化，返回的是boolean
        if (redisTemplate.opsForValue().setIfAbsent(lockKey, String.valueOf(endTime))) {
            redisTemplate.expire(lockKey, 30, TimeUnit.SECONDS);
            return true;
        }
        // 避免死锁，且只让一个线程拿到锁
        String currentValue = (String) redisTemplate.opsForValue().get(lockKey);
        // 如果锁过期了
        if (!StringUtils.isEmpty(currentValue) && Long.parseLong(currentValue) < System.currentTimeMillis()) {
            // 获取上一个锁的时间
            String oldValues = (String) redisTemplate.opsForValue().getAndSet(lockKey, String.valueOf(endTime));
            // 只会让一个线程拿到锁
            // 如果旧的value和currentValue相等，只会有一个线程达成条件，因为第二个线程拿到的oldValue已经和currentValue不一样了
            if (!StringUtils.isEmpty(oldValues) && oldValues.equals(currentValue)) {
                redisTemplate.expire(lockKey, 30, TimeUnit.SECONDS);
                return true;
            }
        }
        return false;
    }


    /**
     * @param lockKey
     * @Title: unLock
     * @Description: 解锁操作
     * @author Lin.Xu - 2020年1月3日[下午3:51:02]
     */
    @Override
    public void unLock(String lockKey) {
        log.info("key:{}--开始解锁", lockKey);
        try {
            String currentValue = (String) redisTemplate.opsForValue().get(lockKey);
            if (!StringUtils.isEmpty(currentValue)) {
                redisTemplate.opsForValue().getOperations().delete(lockKey);
            }
        } catch (Exception e) {
            log.error("『redis分布式锁』解锁异常，{}", e);
        }
    }


}
